// Bitmap Font
// (c) jimon game studio

#include "CBitmapFont.h"
#include "ILogManager.h"

namespace je
{
	namespace video
	{
		//! Constructor
		CBitmapFont::CBitmapFont(core::IBuffer * Buffer)
		{
			core::jeJED_DataBlock * RootBlock = core::GetJEDManager()->Load(Buffer);

			for(core::jeJED_DataBlock::ChildrensListType::Iterator Iter = RootBlock->GetChildrens().begin();Iter != RootBlock->GetChildrens().end();Iter++)
				if((*Iter)->GetId() == core::JEDDBT_BITMAPFONT)
				{
					core::jeJED_BitmapFontBlock * FontBlock = static_cast<core::jeJED_BitmapFontBlock*>(*Iter);
					FontImageName = FontBlock->GetFontImageName().c_str();
					FontSize = FontBlock->GetFontSize();

					for(core::jeJED_BitmapFontBlock::FontCharListType::Iterator CharIter = FontBlock->GetCharsList().begin();CharIter!=FontBlock->GetCharsList().end();CharIter++)
					{
						CharMetrics * TempChar = NULL;
						JENEW(TempChar,CharMetrics)

						TempChar->Code = (*CharIter)->Code;
						TempChar->U1 = (*CharIter)->U1;
						TempChar->V1 = (*CharIter)->V1;
						TempChar->U2 = (*CharIter)->U2;
						TempChar->V2 = (*CharIter)->V2;
						TempChar->ShiftX = (*CharIter)->ShiftX;
						TempChar->ShiftY = (*CharIter)->ShiftY;
						TempChar->SizeX = (*CharIter)->SizeX;
						TempChar->SizeY = (*CharIter)->SizeY;
						TempChar->CharShift = (*CharIter)->CharShift;

						FontChars.InsertElement(TempChar);
					}

					for(core::jeJED_BitmapFontBlock::FontKerningListType::Iterator KerningIter = FontBlock->GetKerningList().begin();KerningIter!=FontBlock->GetKerningList().end();KerningIter++)
					{
						FontKerningMapType::Node * Node = FontKerningMap.find((*KerningIter)->Code1);
						FontKerningInternalMapType * InternalMap = NULL;
						if(Node)
							InternalMap = Node->getValue();
						else
						{
							JENEW(InternalMap,FontKerningInternalMapType)
							FontKerningMap.set((*KerningIter)->Code1,InternalMap);
						}

						InternalMap->set((*KerningIter)->Code2,jeVector2df((*KerningIter)->X,(*KerningIter)->Y));
					}
				}

			ImageResource = JE_IMAGERESOURCE(FontImageName.c_str());
			ImageResource->AddUsage();

			RootBlock->Drop();
		}

		//! Destructor
		CBitmapFont::~CBitmapFont()
		{
			ImageResource->RemoveUsage();

			for(FontKerningMapType::Iterator Iter = FontKerningMap.getIterator();!Iter.atEnd();Iter++)
				JEDELETE(Iter->getValue())

			for(u32 i=0;i<FontChars.GetSize();i++)
				JEDELETE(FontChars[i])
			FontChars.Clear();
		}

		//! Render Text
		void CBitmapFont::RenderText(IImage * Image,const jeStringd & Text,const jeVector2di & DrawPosition,u1 EnableKerning,u1 EnableCharsAlphaBlending)
		{
			jeVector2di ImgSize = Image->GetSize();
			jeVector2di CurrentDrawPosition = DrawPosition;

			Image->Lock();

			IImage * SrcImg = ImageResource->GetImage();
			SrcImg->Lock();

			for(u32 i=0;i<Text.GetSize();i++)
			{
				jeschar SChar = Text[i];

				u32 CharIndex = 0;
				u1 LoadedChar = false;

				for(u32 CharIterator=0;CharIterator<FontChars.GetSize();CharIterator++) // TODO change it to binary find
				{
					if(FontChars[CharIterator]->Code == SChar)
					{
						CharIndex = CharIterator;
						LoadedChar = true;
						break;
					}
				}

				if(!LoadedChar)
				{
					//JELOG_W(jeStringc("Font dont support char : '")+jeStringc(SChar)+jeStringc("', ignoring"))
					continue;
				}

				jeVector2di DrawPos = CurrentDrawPosition+jeVector2di((s32)FontChars[CharIndex]->ShiftX,(s32)FontChars[CharIndex]->ShiftY);

				//jeVector2di DrawSize((s32)FontChars[CharIndex]->SizeX,(s32)FontChars[CharIndex]->SizeY);
				//DrawSize.X = jeMax(0,DrawSize.X);
				//DrawSize.Y = jeMax(0,DrawSize.Y);

				jeVector2di SrcPos((s32)(FontChars[CharIndex]->U1 * SrcImg->GetSize().X),(s32)(FontChars[CharIndex]->V1 * SrcImg->GetSize().Y));

				jeVector2di DrawSize((s32)(FontChars[CharIndex]->U2 * SrcImg->GetSize().X),(s32)(FontChars[CharIndex]->V2 * SrcImg->GetSize().Y));
				DrawSize -=SrcPos;
				DrawSize.X = jeMax(0,DrawSize.X);
				DrawSize.Y = jeMax(0,DrawSize.Y);

				jeVector2di CurrentKerning(0,0);
				if(EnableKerning && (i>0))
				{
					jeVector2df Kerning = GetKerning(Text[i-1],Text[i]);
					CurrentKerning = jeVector2di((s32)Kerning.X,(s32)Kerning.Y);
					DrawPos = DrawPos + CurrentKerning;
				}

				if(DrawPos.X<0)
				{
					SrcPos.X -= DrawPos.X;
					DrawSize.X += DrawPos.X;
					DrawPos.X = 0;
				}

				if(DrawPos.Y<0)
				{
					SrcPos.Y -= DrawPos.Y;
					DrawSize.Y += DrawPos.Y;
					DrawPos.Y = 0;
				}

				Image->DrawImageOnImage(SrcImg,DrawPos,SrcPos,DrawSize,EnableCharsAlphaBlending);
				CurrentDrawPosition.X += (s32)FontChars[CharIndex]->SizeX + (s32)FontChars[CharIndex]->CharShift + CurrentKerning.X;
			}

			SrcImg->UnLock();

			Image->UnLock();
		}

		//! Calculate Text Size
		jeVector2di CBitmapFont::CalculateTextSize(const jeStringd & Text,u1 EnableKerning)
		{
			jeVector2di TextSize;
			for(u32 i=0;i<Text.GetSize();i++)
			{
				jeschar SChar = Text[i];

				u32 CharIndex = 0;
				u1 LoadedChar = false;

				for(u32 CharIterator=0;CharIterator<FontChars.GetSize();CharIterator++) // TODO change it to binary find
				{
					if(FontChars[CharIterator]->Code == SChar)
					{
						CharIndex = CharIterator;
						LoadedChar = true;
						break;
					}
				}

				if(!LoadedChar)
				{
					//JELOG_W(jeStringc("Font dont support char : '")+jeStringc(SChar)+jeStringc("', ignoring"))
					continue;
				}

				TextSize.Y = jeMax((s32)FontChars[CharIndex]->ShiftY+(s32)FontChars[CharIndex]->SizeY,TextSize.Y);
				TextSize.X += (s32)FontChars[CharIndex]->SizeX+(s32)FontChars[CharIndex]->CharShift;
				if(EnableKerning && (i>0))
					TextSize.X += (s32)GetKerning(Text[i-1],Text[i]).X;
			}
			return TextSize;
		}

		//! Get Font Image
		video::IImage * CBitmapFont::GetFontImage()
		{
			return ImageResource->GetImage();
		}

		//! Get Font Resource
		//! If return null, use GetFontImage instead
		media::jeImageResource * CBitmapFont::GetFontResource()
		{
			return ImageResource;
		}

		//! Get Char Metrics
		IFont::CharMetrics * CBitmapFont::GetCharMetrics(je_wchar_t Code)
		{
			for(u32 cti=0;cti<FontChars.GetSize();cti++)
				if(FontChars[cti]->Code == Code)
					return FontChars[cti];
			return NULL;
		}

		//! Get Kerning
		jeVector2df CBitmapFont::GetKerning(je_wchar_t PreviousChar,je_wchar_t CurrentChar)
		{
			FontKerningMapType::Node * Node = FontKerningMap.find(PreviousChar);
			FontKerningInternalMapType * InternalMap = NULL;
			if(Node)
				InternalMap = Node->getValue();
			else
				return jeVector2df(0,0);

			FontKerningInternalMapType::Node * NodeInternal = InternalMap->find(CurrentChar);
			if(NodeInternal)
				return NodeInternal->getValue();
			else
				return jeVector2df(0,0);
		}
	}
}
